AtCoder ABC 038 C - 単調増加 (100点)
https://gyazo.com/5d84931ceb06d02d0add0b7f19bc1818
考察履歴
全探索する場合を考えると、以下$ O(N^2)になる
code:疑似
for i in 0..N
for j in i..N
count += 1
else
break
当然TLEするのでNG
単調増加を見つけて、処理していくので尺取法でできそうと考えた
が、うまく数え上げる方法がわからず、断念
解説見ると、尺取法で考えるのがスタンダードなよう
別途学習する
数学を使って解くようなことを考えた
単調増加の要素数がわかれば、そこでの数え上げは数列の法則から導けるのではないかと考えた
単調増加の要素数
1個:1パターン
2個:3パターン
3個:6パターン
4個:10パターン
つまり、$ a_n = 1,3,6,10,...の数列に対して
階差数列 $ b_n = 2,3,4,...が得られる
階差数列の一般項は$ b_n = n+1なので
$ a_n = 1 + (n-1)(n+2)/2となる
ここらへんの導出は等差数列、階差数列の和の公式を使って行える
$ a_nが得られたので、あとは単調増加の要素数を求めれば良い
同じ数字が続く部分は単調増加ではないので、除外する必要がある
ここの処理を簡単にするため、入力された$ N個の数列をランレングス圧縮する
その後、単調増加を判定していく
これで単調増加の要素数が求まるので、それらに$ a_nの式を当てはめansを追加していく
また、$ l=rの1パターンの考慮が必要
これは、$ N-ランレングス圧縮結果の要素数で求まる
これもansに追加する
以上でACできた。
が、尺取法での実装がスタンダード
尺取法での検討
本問題での「単調増加」という条件が、「条件を満たしている区間の部分区間も条件を満たす」構造をしているので尺取法が適用可能
res += right - left; で数え上げていく
今回の問題で、尺取法がOKでは?と注意することはできた
次回また気になったら、もう少し深く検討するようにする
実装
C++で行ったが、いろいろ初歩的なところでつまずいた
code:cpp
vector<ll> a(n); REP(i,n){cin >> ai;} //vectorのサイズを初期化しないと、aiで初期化できない。 code:cpp
vector<std::pair<ll, ll>> rle = rle(a); //変数名をrleにするとエラー
pointer-to-function 型に対する適切な operator() または変換関数のないクラス型のオブジェクトの呼び出しです
とエラー。変数名をa_rleなどに変えるとOK。別の変数ではなく関数ポインタと認識されるのか?詳細あまり理解できていないが、エラーとのこと。
ACコード
code:cpp
using namespace std;
typedef long long ll;
#define FOR(i,a,b) for(ll i=a;i<=(ll)(b);i++) #define REP(i,n) for(ll i=0;i<(ll)(n);i++) #define REPD(i,n) for(ll i=n-1;i>=0;i--) #define SORT(s) sort((s).begin(),(s).end()) vector<std::pair<ll, ll>> rle(const vector<ll> v){
vector<std::pair<ll, ll>> res;
ll pre = -1;
ll chain = 1;
for(auto iter = begin(v); iter != end(v); ++iter){
if(*iter == pre){
chain += 1;
}else{
if(pre != -1){
res.push_back(make_pair(pre,chain));
}
pre = *iter;
chain = 1;
}
}
res.push_back(make_pair(pre, chain));
return res;
}
int main(){
ll n; cin >> n;
vector<ll> a(n); REP(i,n){cin >> ai;} //vectorのサイズを初期化しないと、aiで初期化できない。 vector<std::pair<ll, ll>> a_rle = rle(a); //変数名をrleにするとエラー
vector<ll> increase;
ll count = 1;
for(auto i = 1; i < a_rle.size(); ++i){
if(a_rlei-1.first < a_rlei.first){ count += 1;
}else{
increase.push_back(count);
count = 1;
}
}
increase.push_back(count);
ll ans = 0;
for(auto iter = begin(increase); iter != end(increase); ++iter){
ans += 1 + ((*iter-1) * (*iter+2)) / 2;
}
ans += n - a_rle.size();
cout << ans << endl;
}